/*
 *  Copyright (C) 2006 Takeharu KATO
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */

#ifdef HAVE_CONFIG_H
#  include <config.h>
#endif

#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <unistd.h>
#include <gnome.h>
#include <glib.h>
#include "common.h"

#define MSG_STRING         "1:%ld:%s:%s:%lu:%s"
#define MSG_GETINFO_STRING "1:%ld:%s:%s:%lu:"
#define MSG_ABSENT_STRING  "1:%ld:%s:%s:%lu:%s[%s]"
#define MSG_STRING_RECVMSG "1:%ld:%s:%s:%lu:%d"
#define MSG_STRING_INFOMSG "1:%ld:%s:%s:%lu:%s"


static long pkt_no=0;

GStaticMutex pktno_mutex = G_STATIC_MUTEX_INIT;

long
ipmsg_get_pkt_no(void) {
  g_static_mutex_lock(&pktno_mutex);
  ++pkt_no;
  g_static_mutex_unlock(&pktno_mutex);
  return (time(NULL)%100000 + pkt_no);
}

static int
construct_message(const char *base,const char *ext,char **msg,size_t *len){
  char *sp;
  char *basep;
  char *extp;
  size_t base_len=0;
  size_t ext_len=0;
  size_t buff_len;
  char *buffer;

  if ( (!msg) || (*msg) || (!len) )
    return -EINVAL;

  base_len=strlen(base)+1;  /*ヌル終端をふくめた長さ */
  if (ext)
    ext_len = strlen(ext)+1;
  buff_len=base_len+ext_len;
  buffer=g_malloc(buff_len);

  if (!buffer) 
    return -ENOMEM;

  sp=basep=buffer;

  strncpy(sp,base,strlen(base)); 
  sp += strlen(base);
  *sp='\0';
  ++sp;
  extp=sp;
  if (ext) {
    strncpy(sp,ext,strlen(ext));
    sp += strlen(ext);
    *sp='\0';
  }
  if (ext)
    dbg_out("Constructed: base:%s ext:%s\n",basep,extp);
  else
    dbg_out("Constructed: base:%s\n",basep);
  *len=buff_len; /* 送信するデータ長を答える */
  *msg=buffer;

  return 0;
}
static int
ipmsg_construct_file_attach_message(const char *msg_string,const char *ext_part,int flags,size_t *len,char **msg,long pktno){
  char base[IPMSG_BUFSIZ];
  char *message;
  size_t msg_len;
  int rc;


  if ( (!msg_string) || (!ext_part) || (!msg) || (!len) )
    return -EINVAL;
  if (!(flags & IPMSG_FILEATTACHOPT))
    return -EINVAL;

  dbg_out("ext:%s\n",ext_part);

  memset(base,0,IPMSG_BUFSIZ);

  snprintf(base,IPMSG_BUFSIZ-1,MSG_STRING,
	   pktno,
	   hostinfo_refer_user_name(),
	   hostinfo_refer_host_name(),
	   IPMSG_SENDMSG|flags,
	   msg_string);



  message=NULL;
  rc=construct_message(base,ext_part,&message,&msg_len);
  *len=msg_len;
  if (rc<0)
    return rc;

  *msg=message;


  return 0;
}
static int
ipmsg_construct_normal_message(const char *msg_string,int flags,size_t *len,char **msg,long pktno){
  char base[IPMSG_BUFSIZ];
  char *message;
  size_t msg_len;
  int rc;

  if ( (!msg_string) || (!msg) || (!len) )
    return -EINVAL;


  memset(base,0,IPMSG_BUFSIZ);

  snprintf(base,IPMSG_BUFSIZ-1,MSG_STRING,
	   pktno,
	   hostinfo_refer_user_name(),
	   hostinfo_refer_host_name(),
	   IPMSG_SENDMSG|flags,
	   msg_string);

  message=NULL;
  rc=construct_message(base,NULL,&message,&msg_len);

  if (rc<0)
    return rc;

  *msg=message;
  *len=msg_len;

  return 0;
}
int
ipmsg_construct_getfile_message(unsigned long ftype,const char *msg_string,int flags,size_t *len,char **msg,long pktno){
  char base[IPMSG_BUFSIZ];
  char *message;
  size_t msg_len;
  unsigned long local_flags;
  int rc;

  if ( (!msg_string) || (!msg) || (!len) )
    return -EINVAL;
  
  local_flags=flags;
  local_flags|=((ftype == IPMSG_FILE_DIR)?
		(IPMSG_GETDIRFILES):
		(IPMSG_GETFILEDATA));
  local_flags|=hostinfo_get_normal_send_flags();

  memset(base,0,IPMSG_BUFSIZ);

  snprintf(base,IPMSG_BUFSIZ-1,MSG_STRING,
	   pktno,
	   hostinfo_refer_user_name(),
	   hostinfo_refer_host_name(),
	   local_flags,
	   msg_string);

  message=NULL;
  rc=construct_message(base,NULL,&message,&msg_len);

  if (rc<0)
    return rc;

  *msg=message;
  *len=msg_len;

  return 0;
}

static int
ipmsg_send_absent_msg(const udp_con_t *con,const int flags){
  char *absent_message=NULL;
  gchar *absent_message_proto=NULL;
  char  *send_string;
  int index;
  size_t len;
  int local_flags;
  int rc;
  char *ipaddr;

  rc=-EINVAL;
  if (!con)
    goto no_need_free_out;

  local_flags=(flags|IPMSG_AUTORETOPT);
  local_flags &= ~(IPMSG_SENDCHECKOPT|IPMSG_ENCRYPTOPT);

  g_assert(!hostinfo_get_absent_id(&index));
  rc=hostinfo_get_absent_message(index,(const char **)&absent_message);
  if (!absent_message)
    goto no_need_free_out;

  convert_string_ipmsg_proto(absent_message,(const gchar **)&absent_message_proto);
  if (!absent_message_proto) 
    goto free_absent_message;

  pkt_no=ipmsg_get_pkt_no();
  rc=ipmsg_construct_normal_message(absent_message_proto,local_flags,&len,&send_string,pkt_no);
  if (rc<0){
    if (send_string)
      goto free_send_string;
  }
  if (!send_string)
    goto free_absent_message;

  ipaddr=(char *)udp_get_peeraddr(con);

  dbg_out("send absent message to %s:%s\n",ipaddr,send_string);

  rc=udp_send_peer(con,send_string,len);
  if (rc<0) {
    rc*=-1;
    ipmsg_err_dialog(_("Can not send message to %s. reason: %s error code = %d"), ipaddr, strerror(rc), rc);
    goto free_send_string;
  }

  logfile_send_log(ipaddr,absent_message);
  rc=0;

free_send_string:
  g_free(send_string);
free_absent_message_proto:
  g_free (absent_message_proto);
free_absent_message:
  g_free(absent_message);
no_need_free_out:
  return rc;
}

int
ipmsg_send_br_isgetlist2(const udp_con_t *con,const int flags){
  char base[IPMSG_BUFSIZ];
  char *msg=NULL;
  size_t len;
  int rc;
  unsigned long local_flags;

  local_flags  = flags;
  local_flags |= hostinfo_get_normal_entry_flags();
  local_flags |= IPMSG_BR_ISGETLIST2;

  memset(base,0,IPMSG_BUFSIZ);
  snprintf(base,IPMSG_BUFSIZ-1,MSG_STRING,ipmsg_get_pkt_no(),hostinfo_refer_user_name(),hostinfo_refer_host_name(),local_flags,"");

  rc=construct_message(base,NULL,&msg,&len);
  if (rc<0)
    return rc;

  ipmsg_send_broad_cast(con,msg,len);
  g_free(msg);

  return 0;
}

int
ipmsg_send_br_entry(const udp_con_t *con,const int flags){
  char base[IPMSG_BUFSIZ];
  char ext[IPMSG_BUFSIZ];
  char *msg=NULL;
  size_t len;
  int rc;
  unsigned long local_flags;

  if (hostinfo_refer_ipmsg_is_get_hlist())
    ipmsg_send_br_isgetlist2(con,flags);

  local_flags  = flags;
  dbg_out("BR-Entry given Flag :%x\n",local_flags);
  local_flags |= hostinfo_get_normal_entry_flags();
  local_flags |= IPMSG_BR_ENTRY;
  dbg_out("BR-Entry Flag :%x\n",local_flags);
  memset(base,0,IPMSG_BUFSIZ);
  memset(ext,0,IPMSG_BUFSIZ);
  snprintf(base,IPMSG_BUFSIZ-1,MSG_STRING,ipmsg_get_pkt_no(),hostinfo_refer_user_name(),hostinfo_refer_host_name(),local_flags,hostinfo_refer_nick_name_proto());
  snprintf(ext,IPMSG_BUFSIZ-1,"%s",hostinfo_refer_group_name_proto());
  if (hostinfo_is_ipmsg_absent()) {
    char *absent_title=NULL;
    gchar *absent_title_proto=NULL;
    int index;

    if (!hostinfo_get_absent_id(&index)) {
      hostinfo_get_absent_title(index,(const char **)&absent_title);
      g_assert (absent_title);
      convert_string_ipmsg_proto(absent_title,(const gchar **)&absent_title_proto);
      g_free(absent_title);
      g_assert (absent_title_proto);
      if (absent_title_proto) {
	snprintf(base,IPMSG_BUFSIZ-1,MSG_ABSENT_STRING,ipmsg_get_pkt_no(),hostinfo_refer_user_name(),hostinfo_refer_host_name(),local_flags,hostinfo_refer_nick_name_proto(),absent_title_proto);
	g_free (absent_title_proto);
      }
    }
  }

  rc=construct_message(base,ext,&msg,&len);
  if (rc<0)
    return rc;

  ipmsg_send_broad_cast(con,msg,len);
  g_free(msg);

  return 0;
}

int
ipmsg_send_br_exit(const udp_con_t *con,const int flags){
  char base[IPMSG_BUFSIZ];
  char ext[IPMSG_BUFSIZ];
  char *msg=NULL;
  size_t len;
  int rc;
  unsigned long local_flags;

  local_flags  = flags;
  local_flags |= hostinfo_get_normal_entry_flags();
  local_flags |= IPMSG_BR_EXIT;

  memset(base,0,IPMSG_BUFSIZ);
  memset(ext,0,IPMSG_BUFSIZ);
  snprintf(base,IPMSG_BUFSIZ-1,MSG_STRING,ipmsg_get_pkt_no(),hostinfo_refer_user_name(),hostinfo_refer_host_name(),local_flags,hostinfo_refer_nick_name_proto());
  snprintf(ext,IPMSG_BUFSIZ-1,"%s",hostinfo_refer_group_name_proto());

  rc=construct_message(base,ext,&msg,&len);
  if (rc<0)
    return rc;

  ipmsg_send_broad_cast(con,msg,len);
  g_free(msg);

  return 0;
}
int
ipmsg_send_br_absence(const udp_con_t *con,const int flags){
  char base[IPMSG_BUFSIZ];
  char ext[IPMSG_BUFSIZ];
  char *msg=NULL;
  size_t len;
  int rc;
  unsigned long local_flags;

  local_flags  = flags;
  local_flags |= hostinfo_get_normal_entry_flags();
  local_flags |= IPMSG_BR_ABSENCE;

  memset(base,0,IPMSG_BUFSIZ);
  memset(ext,0,IPMSG_BUFSIZ);

  snprintf(base,IPMSG_BUFSIZ-1,MSG_STRING,ipmsg_get_pkt_no(),hostinfo_refer_user_name(),hostinfo_refer_host_name(),local_flags,hostinfo_refer_nick_name_proto());
  base[IPMSG_BUFSIZ-1]='\0';

  snprintf(ext,IPMSG_BUFSIZ-1,"%s",hostinfo_refer_group_name_proto());
  ext[IPMSG_BUFSIZ-1]='\0';

  if (hostinfo_is_ipmsg_absent()) {
    char *absent_title=NULL;
    gchar *absent_title_proto=NULL;
    int index;

    if (!hostinfo_get_absent_id(&index)) {
      hostinfo_get_absent_title(index,(const char **)&absent_title);
      g_assert (absent_title);
      convert_string_ipmsg_proto(absent_title,(const gchar **)&absent_title_proto);
      g_free(absent_title);
      g_assert (absent_title_proto);

      if (absent_title_proto) {
	memset(base,0,IPMSG_BUFSIZ);
	snprintf(base,IPMSG_BUFSIZ-1,MSG_ABSENT_STRING,
		 ipmsg_get_pkt_no(),
		 hostinfo_refer_user_name(),
		 hostinfo_refer_host_name(),
		 local_flags,
		 hostinfo_refer_nick_name_proto(),
		 absent_title_proto);
	base[IPMSG_BUFSIZ-1]='\0';
	g_free (absent_title_proto);
      }
    }
  }

  rc=construct_message(base,ext,&msg,&len);
  if (rc<0)
    return rc;

  ipmsg_send_broad_cast(con,msg,len);
  g_free(msg);

  return 0;
}

static int
ipmsg_send_ans_entry_common(const udp_con_t *con, const char *ipaddr, const int flags){
  char base[IPMSG_BUFSIZ];
  char ext[IPMSG_BUFSIZ];
  char *msg=NULL;
  size_t len;
  int rc;
  unsigned long local_flags = flags;

  local_flags |= IPMSG_ANSENTRY;

  if (hostinfo_is_ipmsg_absent()) 
    local_flags |= IPMSG_ABSENCEOPT;

  memset(base,0,IPMSG_BUFSIZ);
  memset(ext,0,IPMSG_BUFSIZ);

  snprintf(base,IPMSG_BUFSIZ-1,MSG_STRING,ipmsg_get_pkt_no(),hostinfo_refer_user_name(),hostinfo_refer_host_name(),local_flags,hostinfo_refer_nick_name_proto());

  snprintf(ext,IPMSG_BUFSIZ-1,"%s",hostinfo_refer_group_name_proto());
  if (hostinfo_is_ipmsg_absent()) {
    char *absent_title=NULL;
    gchar *absent_title_proto=NULL;
    int index;

    if (!hostinfo_get_absent_id(&index)) {
      hostinfo_get_absent_title(index,(const char **)&absent_title);
      g_assert (absent_title);
      convert_string_ipmsg_proto(absent_title,(const gchar **)&absent_title_proto);
      g_free(absent_title);
      g_assert (absent_title_proto);
      if (absent_title_proto) {
	snprintf(ext,IPMSG_BUFSIZ-1,"%s[%s]",hostinfo_refer_group_name_proto(),absent_title_proto);
	g_free (absent_title_proto);
      }
    }
  }

  rc = construct_message(base,ext,&msg,&len);
  if (rc<0)
    return rc;

  dbg_out("AnserEntry:%s\n",msg);

  udp_send_message(con, ipaddr, hostinfo_refer_ipmsg_port(), msg, len);

  g_free(msg);

  return 0;
}

int
ipmsg_send_gratuitous_ans_entry(const udp_con_t *con, const char *ipaddr, const int flags){
  int rc = 0;

  if ( (con == NULL) || (ipaddr == NULL) )
    return -EINVAL;

  /*  無条件に送りつけるANS_ENTRYなので, flagsは呼び出し側責任で設定する.
   *  これは, 暗号化通信のキャンセルなどで使用する.
   */

  dbg_out("Send gratutous ANS_ENTRY to %s with 0x%08x\n",
	  ipaddr, flags);

  rc = ipmsg_send_ans_entry_common(con, ipaddr, flags);

  return rc;
}

int
ipmsg_send_ans_entry(const udp_con_t *con,const int flags){
  char base[IPMSG_BUFSIZ];
  char ext[IPMSG_BUFSIZ];
  char *msg=NULL;
  size_t len;
  int rc;
  unsigned long local_flags;

  local_flags  = flags;
  local_flags |= hostinfo_get_normal_entry_flags();

  /*
   * 応答用ANS_ENTRYを返す.
   */
  rc = ipmsg_send_ans_entry_common(con, udp_get_peeraddr(con), local_flags);

  return rc;
}
int
ipmsg_send_recv_msg(const udp_con_t *con,const msg_data_t *orig_msg){
  char base[IPMSG_BUFSIZ];
  char *msg=NULL;
  size_t len;
  int rc;

  memset(base,0,IPMSG_BUFSIZ);

  snprintf(base,IPMSG_BUFSIZ-1,MSG_STRING_RECVMSG,ipmsg_get_pkt_no(),hostinfo_refer_user_name(),hostinfo_refer_host_name(),(IPMSG_RECVMSG|IPMSG_AUTORETOPT),refer_pkt_no_name_from_msg(orig_msg));

  rc=construct_message(base,NULL,&msg,&len);
  if (rc<0)
    return rc;
  udp_send_peer(con,msg,len);
  g_free(msg);

  return 0;
}
int
ipmsg_send_release_files(const udp_con_t *con,const char *ipaddr,int pktno){
  char base[IPMSG_BUFSIZ];
  char *msg=NULL;
  size_t len;
  int rc;

  memset(base,0,IPMSG_BUFSIZ);

  snprintf(base,IPMSG_BUFSIZ-1,MSG_STRING_RECVMSG,ipmsg_get_pkt_no(),hostinfo_refer_user_name(),hostinfo_refer_host_name(),IPMSG_RELEASEFILES,pktno);

  rc=construct_message(base,NULL,&msg,&len);
  if (rc<0)
    return rc;
  rc=udp_send_message(con,ipaddr,hostinfo_refer_ipmsg_port(),msg,len);
  g_free(msg);

  if (rc<0)
    return rc;

  return 0;
}

int
ipmsg_send_get_list(const udp_con_t *con,const char *ipaddr,int index){
  char base[IPMSG_BUFSIZ];
  char *msg=NULL;
  size_t len;
  int rc;

  if ( (!con) || (!ipaddr) )
    return -EINVAL;

  memset(base,0,IPMSG_BUFSIZ);

  snprintf(base,IPMSG_BUFSIZ-1,MSG_STRING_RECVMSG,ipmsg_get_pkt_no(),hostinfo_refer_user_name(),hostinfo_refer_host_name(),IPMSG_GETLIST,index);

  rc=construct_message(base,NULL,&msg,&len);
  if (rc<0)
    return rc;
  rc=udp_send_message(con,ipaddr,hostinfo_refer_ipmsg_port(),msg,len);
  g_free(msg);

  if (rc<0)
    return rc;

  return 0;
}

int
ipmsg_send_read_msg(const udp_con_t *con,const char *ipaddr,int pktno){
  char base[IPMSG_BUFSIZ];
  char *msg=NULL;
  size_t len;
  int rc;

  memset(base,0,IPMSG_BUFSIZ);

  snprintf(base,IPMSG_BUFSIZ-1,MSG_STRING_RECVMSG,ipmsg_get_pkt_no(),hostinfo_refer_user_name(),hostinfo_refer_host_name(),IPMSG_READMSG,pktno);

  rc=construct_message(base,NULL,&msg,&len);
  if (rc<0)
    return rc;
  rc=udp_send_message(con,ipaddr,hostinfo_refer_ipmsg_port(),msg,len);
  g_free(msg);

  if (rc<0)
    return rc;

  return 0;
}

int
ipmsg_send_get_info_msg(const udp_con_t *con,const char *ipaddr,unsigned long command){
  char base[IPMSG_BUFSIZ];
  char *send_string=NULL;
  size_t len;
  int rc;

  if ( (!con) || (!ipaddr) || ( (command != IPMSG_GETINFO) && (command != IPMSG_GETABSENCEINFO) ) )
    return -EINVAL;

  memset(base,0,IPMSG_BUFSIZ);

  snprintf(base,IPMSG_BUFSIZ-1,MSG_GETINFO_STRING,ipmsg_get_pkt_no(),hostinfo_refer_user_name(),hostinfo_refer_host_name(),command);

  rc=construct_message(base,NULL,&send_string,&len);
  if (rc<0)
    return rc;

  rc=udp_send_message(con,ipaddr,hostinfo_refer_ipmsg_port(),send_string,len);
  g_free(send_string);

  if (rc<0)
    return rc;

  return 0;
}
int
ipmsg_send_getpubkey(const udp_con_t *con,const char *ipaddr){
  int rc;
  char *message=NULL;
  char base[IPMSG_BUFSIZ];
  char *send_string=NULL;
  size_t len;

  if ( (!con) || (!ipaddr) )
    return -EINVAL;

#if defined(USE_OPENSSL)
  memset(base,0,IPMSG_BUFSIZ);

  rc=pcrypt_crypt_generate_getpubkey_string(hostinfo_get_ipmsg_crypt_capability(),(const char **)&message);
  if (rc)
    return rc;

  snprintf(base,IPMSG_BUFSIZ-1,MSG_STRING,ipmsg_get_pkt_no(),hostinfo_refer_user_name(),hostinfo_refer_host_name(),IPMSG_GETPUBKEY,message);

  rc=construct_message(base,NULL,&send_string,&len);
  if (rc<0)
    goto free_message_out;

  rc=udp_send_message(con,ipaddr,hostinfo_refer_ipmsg_port(),send_string,len);
  g_free(send_string);

  if (rc<0)
    goto free_message_out;

  rc=0;

 free_message_out:
  if (message)
    g_free(message);
#else
  rc=-ENOSYS;
#endif  /*   USE_OPENSSL  */
  return rc;
}
int
ipmsg_send_send_msg(const udp_con_t *con,const char *ipaddr,int flags,int pkt_no,const char *message,const char *ext_part){
  int rc;
  size_t len;
  char *send_string=NULL;
  char *sent_message=NULL;
  size_t sent_msg_len;
  gchar *internal_message=NULL;
  unsigned long peer_cap=0;

  if ( (!con) || (!ipaddr) || (!message) )
    return -EINVAL;

  if ( (flags & IPMSG_FILEATTACHOPT) && (!ext_part) )
    return -EINVAL;

#if defined(USE_OPENSSL)
  if (flags & IPMSG_ENCRYPTOPT) {
    rc=userdb_get_basic_cap_by_addr(ipaddr,&peer_cap);
    if ( (rc) || (!(peer_cap & IPMSG_ENCRYPTOPT)) ) {
      dbg_out("Peer can not handle crypted message.rc=%d cap=%x\n",rc,peer_cap);
      flags &= ~IPMSG_ENCRYPTOPT;
      sent_message=(char *)message;
    }else{
      dbg_out("Peer's cap=%x\n",peer_cap);
      rc=ipmsg_encrypt_message(ipaddr,message,(unsigned char **)&sent_message,&sent_msg_len);
      if (rc) {
	dbg_out("Peer can not encode the message.rc=%d\n",rc);
	flags &= ~IPMSG_ENCRYPTOPT;
	sent_message=(char *)message;
      }else{
	dbg_out("Encoded message.%s\n",sent_message);
      }
    }
  }
#else
  flags &= ~IPMSG_ENCRYPTOPT;
  sent_message=(char *)message;
#endif  /*  USE_OPENSSL  */

  if (flags & IPMSG_FILEATTACHOPT)
    rc=ipmsg_construct_file_attach_message(sent_message,ext_part,flags,&len,&send_string,pkt_no);
  else
    rc=ipmsg_construct_normal_message(sent_message,flags,&len,&send_string,pkt_no);

#if !defined(USE_OPENSSL)
  g_assert(!(flags & IPMSG_ENCRYPTOPT));
#endif  /*  USE_OPENSSL  */

  if (flags & IPMSG_ENCRYPTOPT) {
    g_assert(sent_message);
    g_free(sent_message);
  }

  if (rc<0){
    if (send_string)
      g_free(send_string);
    return rc;
  }

  dbg_out("send message to %s:%s\n",ipaddr,send_string);
  rc=register_sent_message(con,ipaddr,pkt_no,send_string,len);
  if (rc<0) {
    rc *= -1;
    ipmsg_err_dialog(_("Can not register message for:%s reason: %s error code = %d"), ipaddr, strerror(rc), rc);
    return -rc;
  }

  rc=udp_send_message(con,ipaddr,hostinfo_refer_ipmsg_port(), send_string,len);
  if (rc<0) {
    rc*=-1;
    ipmsg_err_dialog(_("Can not send message to %s. reason: %s error code = %d"), ipaddr, strerror(rc), rc);
    goto error_out;
  }

  convert_string_internal(message,(const gchar **)&internal_message);
  if (internal_message) {
    logfile_send_log(ipaddr, internal_message);
    g_free(internal_message);
  } else {
    ipmsg_err_dialog(_("Can not convert message for %s into ineternal representation"), ipaddr);
  }

  rc=0;
 error_out:
  g_free(send_string);
  return rc;
}
int
ipmsg_proc_br_absence(const udp_con_t *con,const msg_data_t *msg){
  dbg_out("\nNew user: \n\tUser:%s Host: %s\n\tNickName: %s GroupName:%s\n",
	  refer_user_name_from_msg(msg),
	  refer_host_name_from_msg(msg),
	  refer_nick_name_from_msg(msg),
	  refer_group_name_from_msg(msg));

  userdb_update_user(con,msg);
  userdb_print_user_list();

  return 0;
}
int
ipmsg_proc_br_entry(const udp_con_t *con,const msg_data_t *msg) {
  int rc=0;

  ipmsg_send_ans_entry(con,0);

  rc=userdb_add_user(con,msg);
  if (rc == -EEXIST)
    rc=userdb_update_user(con,msg);
  userdb_print_user_list();

  return rc;
}
int
ipmsg_proc_br_exit(const udp_con_t *con,const msg_data_t *msg) {
  dbg_out("\nLeave user: \n\tUser:%s Host: %s\n\tNickName: %s GroupName:%s\n",
	  refer_user_name_from_msg(msg),
	  refer_host_name_from_msg(msg),
	  refer_nick_name_from_msg(msg),
	  refer_group_name_from_msg(msg));
  userdb_del_user(con,msg);
  userdb_print_user_list();

  return 0;
}

int
ipmsg_proc_ans_entry(const udp_con_t *con,const msg_data_t *msg) {
  dbg_out("\nNew user: \n\tUser:%s Host: %s\n\tNickName: %s GroupName:%s\n",
	  refer_user_name_from_msg(msg),
	  refer_host_name_from_msg(msg),
	  refer_nick_name_from_msg(msg),
	  refer_group_name_from_msg(msg));
  userdb_add_user(con,msg);
  userdb_print_user_list();

  return 0;
}

int
ipmsg_proc_send_msg(const udp_con_t *con,const msg_data_t *msg) {
  gchar *internal_message=NULL;
  int flags;

  if ( (msg->command_opts & IPMSG_SENDCHECKOPT) && 
       (!(msg->command_opts & IPMSG_NO_REPLY_OPTS)) ) {
    dbg_out("Send check opt is enabled\n");
    ipmsg_send_recv_msg(con,msg);
  }
  dbg_out("\nmessage:\n%s\n",msg->message);

  convert_string_internal(msg->message,(const gchar **)&internal_message);
  if (internal_message) {
    if (!(msg->command_opts & IPMSG_NOLOGOPT))
      logfile_recv_log(udp_get_peeraddr(con),internal_message);
    g_free(internal_message);
  } else {
    ipmsg_err_dialog(_("Can not convert message from %s into ineternal representation"), udp_get_peeraddr(con));
  }

  if (hostinfo_is_ipmsg_absent()){
    flags=hostinfo_get_normal_send_flags();
    if (hostinfo_refer_ipmsg_default_secret())
      flags |= IPMSG_SECRETOPT;
    if (!(msg->command_opts & IPMSG_NO_REPLY_OPTS))
      ipmsg_send_absent_msg(con,flags);
  }

  if (hostinfo_refer_ipmsg_default_popup()) /* if it is set, should not pop-up the win */
    store_message_window(msg,udp_get_peeraddr(con));
  else
    recv_message_window(msg,udp_get_peeraddr(con));

  return 0;
}
int
ipmsg_proc_read_msg(const udp_con_t *con,const msg_data_t *msg) {
  long pktno;
  gchar *user;
  struct timeval tv;

  gettimeofday(&tv, NULL);
  pktno=strtol(msg->message, (char **)NULL, 10);
  dbg_out("read mssage:seq %ld\n",pkt_no);

  dbg_out("read mssage:seq %s\n",msg->message);
  user=g_strdup(refer_user_name_from_msg(msg));
  /*  メモリ不足の場合はNULLを送信。処理は受け手で考える  */
  read_message_dialog(user,udp_get_peeraddr(con),tv.tv_sec);

  return 0;
}
static int
ipmsg_proc_anspubkey(const udp_con_t *con,const msg_data_t *msg){
  int rc;
  char *pubkey_e=NULL;
  char *pubkey_n=NULL;
  const char *ipaddr=NULL;
  unsigned long peer_cap;

  if ( (!con) || (!msg) )
    return -EINVAL;

#if defined(USE_OPENSSL)
  rc=pcrypt_crypt_parse_anspubkey(msg->message,
				  &peer_cap,
				  (unsigned char **)&pubkey_e,
				  (unsigned char **)&pubkey_n);
  if (rc) {
    dbg_out("Can not parse anspub key message:rc=%d\n",rc);
    goto error_out;
  }

  ipaddr=udp_get_peeraddr(con);
  if (!ipaddr)
    goto error_out;

  rc=userdb_replace_public_key_by_addr(ipaddr,peer_cap,pubkey_e,pubkey_n);
  if (rc) {
    dbg_out("Can register parse anspub key message:rc=%d\n",rc);
    goto error_out;
  }

  if (pubkey_e)
    g_free(pubkey_e);
  if (pubkey_n)
    g_free(pubkey_n);
#else
  rc=-ENOSYS;
#endif
 error_out:
  return rc;  
}
static int
ipmsg_proc_release_files_msg(const udp_con_t *con,const msg_data_t *msg) {
  int pktno;
  int rc;


  pktno=(int)strtol(msg->message, (char **)NULL, 10);
  dbg_out("release files mssage:seq %d(%s)\n",pktno,msg->message);
  rc = release_attach_file_block(pktno,FALSE);
  if (rc < 0) {
    dbg_out("Can not release attach file information:pktno=%d %s (%d)", 
	    pktno, strerror(-rc), -rc);
    //    ipmsg_err_dialog(_("Can not release attach file information:pktno=%d %s (%d)"),pktno,strerror(-rc),-rc);
  }

  return rc;
}
static int
ipmsg_proc_get_public_key(const udp_con_t *con,const msg_data_t *msg) {
  unsigned long peer_cap;
  char *ans_msg=NULL;
  char base[IPMSG_BUFSIZ];
  int index;
  char *message=NULL;
  size_t len;
  int rc;

  dbg_out("here\n");
#if defined(USE_OPENSSL)
  peer_cap=(int)strtoul(msg->message, (char **)NULL, 16);
  dbg_out("Get public key:peer capability %x(%s)\n",peer_cap,msg->message);
  rc=pcrypt_crypt_generate_anspubkey_string(peer_cap,(const char **)&ans_msg);
  if (rc) {
    if (ans_msg)
      g_free(ans_msg);
    return rc;
  }

  memset(base,0,IPMSG_BUFSIZ);

  snprintf(base,IPMSG_BUFSIZ-1,MSG_STRING_INFOMSG,ipmsg_get_pkt_no(),hostinfo_refer_user_name(),hostinfo_refer_host_name(),(IPMSG_ANSPUBKEY),ans_msg);
  if (ans_msg){
    g_free(ans_msg);
    ans_msg=NULL;
  }
  rc=construct_message(base,NULL,&message,&len);
  if (rc<0)
    return rc;
  udp_send_peer(con,message,len);
  g_free(message);
#else
  rc=-ENOSYS;
#endif  
 error_out:
  return rc;
}
static int
ipmsg_proc_recv_msg(const udp_con_t *con,const msg_data_t *msg) {
  long pktno;

  if ( (!msg) ||  (!(msg->message)))
    return -EINVAL;

  pktno=(long)strtol(msg->message, (char **)NULL, 10);
  dbg_out("recv mssage:seq %ld\n",pkt_no);
  unregister_sent_message(pktno);
  return 0;
}
static int
ipmsg_proc_get_absence_info(const udp_con_t *con,const msg_data_t *orig_msg){
  char base[IPMSG_BUFSIZ];
  char *absent_message=NULL;
  gchar *absent_message_proto=NULL;
  int index;
  char *msg=NULL;
  size_t len;
  int rc;

  dbg_out("here\n");

  memset(base,0,IPMSG_BUFSIZ);

  if (hostinfo_is_ipmsg_absent()) {
    if (!hostinfo_get_absent_id(&index)) {
      hostinfo_get_absent_message(index,(const char **)&absent_message);
      g_assert (absent_message);
    }
    convert_string_ipmsg_proto(absent_message,(const gchar **)&absent_message_proto);
    g_free(absent_message);
  }else{
    absent_message=_("I am here.");
    convert_string_ipmsg_proto(absent_message,(const gchar **)&absent_message_proto);
  }
  g_assert (absent_message_proto);
  
  if (absent_message_proto) {
    snprintf(base,IPMSG_BUFSIZ-1,MSG_STRING_INFOMSG,ipmsg_get_pkt_no(),hostinfo_refer_user_name(),hostinfo_refer_host_name(),(IPMSG_AUTORETOPT|IPMSG_SENDABSENCEINFO),absent_message_proto);
    g_free (absent_message_proto);
  }

  rc=construct_message(base,NULL,&msg,&len);
  if (rc<0)
    return rc;
  udp_send_peer(con,msg,len);
  g_free(msg);

  return 0;
}
static int
ipmsg_proc_send_version_info(const udp_con_t *con,const msg_data_t *orig_msg){
  char base[IPMSG_BUFSIZ];
  char ver[IPMSG_BUFSIZ];
  int index;
  char *message=NULL;
  char *string=NULL;
  size_t len;
  int rc;

  dbg_out("here\n");

  memset(base,0,IPMSG_BUFSIZ);
  memset(ver,0,IPMSG_BUFSIZ);

  snprintf(ver,IPMSG_BUFSIZ-1,"GNOME2 %s (%s)\n%s:%s",_("Edition"),PACKAGE,_("Version"),VERSION);
  rc=convert_string_ipmsg_proto(ver,(const gchar **)&string);
  if (rc<0)
    return rc;
  snprintf(base,IPMSG_BUFSIZ-1,MSG_STRING_INFOMSG,ipmsg_get_pkt_no(),hostinfo_refer_user_name(),hostinfo_refer_host_name(),(IPMSG_AUTORETOPT|IPMSG_SENDINFO),string);
  g_free(string);

  rc=construct_message(base,NULL,&message,&len);
  if (rc<0)
    return rc;
  udp_send_peer(con,message,len);
  g_free(message);

  return 0;
}
static int
ipmsg_proc_br_isgetlist2(const udp_con_t *con,const msg_data_t *msg){
  char base[IPMSG_BUFSIZ];
  char ver[IPMSG_BUFSIZ];
  size_t len;
  char *message=NULL;
  int rc;

  dbg_out("here\n");

  if ( (!con) || (!msg) )
    return -EINVAL;


  memset(base,0,IPMSG_BUFSIZ);
  snprintf(base,IPMSG_BUFSIZ-1,MSG_STRING_INFOMSG,ipmsg_get_pkt_no(),hostinfo_refer_user_name(),hostinfo_refer_host_name(),IPMSG_OKGETLIST,"");

  rc=construct_message(base,NULL,&message,&len);

  if (rc<0)
    return rc;
  dbg_out("isgetlist2 reply message :%s\n",message);
  rc=udp_send_message(udp_con,udp_get_peeraddr(con),hostinfo_refer_ipmsg_port(),message,len);
  g_free(message);

  return 0;
}
static int
ipmsg_proc_getlist(const udp_con_t *con,const msg_data_t *msg){
  char base[IPMSG_BUFSIZ];
  char ver[IPMSG_BUFSIZ];
  int length;
  char *string=NULL;
  gchar *exd_string=NULL;
  unsigned long flags=0;
  int index;
  int start;
  char *message=NULL;
  size_t len;
  int rc;

  dbg_out("here\n");

  if ( (!con) || (!msg) )
    return -EINVAL;

  length=G2IPMSG_DEFAULT_HOST_LIST_LEN;

  start=0;
  start=strtol(msg->message, (char **)NULL, 10);
  dbg_out("require:%d(%s)\n",start,msg->message);
  userdb_get_hostlist_string(start,&length, (const char **)&string,&flags);
  dbg_out("return:%s\n",string);

  rc=convert_string_ipmsg_proto(string,(const gchar **)&exd_string);

  if (rc<0)
    goto free_string_out;

  memset(base,0,IPMSG_BUFSIZ);
  snprintf(base,IPMSG_BUFSIZ-1,MSG_STRING_INFOMSG,ipmsg_get_pkt_no(),hostinfo_refer_user_name(),hostinfo_refer_host_name(),IPMSG_ANSLIST,exd_string);
  g_free(exd_string);

  rc=construct_message(base,NULL,&message,&len);
  if (rc<0)
    goto free_string_out;

  dbg_out("send:%s\n",message);

  if (string) 
    g_free(string);


  rc=udp_send_peer(con,message,len);
  if (rc<0)
    goto free_message_out;
  
  dbg_out("return:%s\n",message);
  g_free(message);

  return 0;

 free_string_out:
  if (string) 
    g_free(string);

 free_message_out:
  if (message) 
    g_free(message);

  return rc;
}
static int
ipmsg_proc_anslist(const udp_con_t *con,const msg_data_t *msg){
  int rc;

  if ( (!con) || (!msg) )
    return -EINVAL;

  rc=userdb_hostlist_answer_add(con,msg);

  return rc;
}
static int
ipmsg_proc_okgetlist(const udp_con_t *con,const msg_data_t *msg){
  char base[IPMSG_BUFSIZ];
  char ver[IPMSG_BUFSIZ];
  size_t len;
  char *message=NULL;
  int rc;
  long pktno;
  dbg_out("here\n");

  if ( (!con) || (!msg) )
    return -EINVAL;

  dbg_out("OK_GETLIST from %s.\n",udp_get_peeraddr(con));

  rc=ipmsg_send_get_list(udp_con,udp_get_peeraddr(con),0);

  return rc;
}
int
ipmsg_proc_send_info(const udp_con_t *con,const msg_data_t *msg) {
  gchar *user;

  dbg_out("send info:version %s\n",msg->message);
  user=g_strdup(refer_user_name_from_msg(msg));
  /*  メモリ不足の場合はNULLを送信。処理は受け手で考える  */
  info_message_window(user,udp_get_peeraddr(con),IPMSG_SENDINFO,msg->message);

  return 0;
}
int
ipmsg_proc_send_absence_info(const udp_con_t *con,const msg_data_t *msg) {
  gchar *user;

  dbg_out("send absence info:version %s\n",msg->message);
  user=g_strdup(refer_user_name_from_msg(msg));
  /*  メモリ不足の場合はNULLを送信。処理は受け手で考える  */
  info_message_window(user,udp_get_peeraddr(con),IPMSG_SENDABSENCEINFO,msg->message);

  return 0;
}

int
ipmsg_dispatch_message(const udp_con_t *con,const msg_data_t *msg){
  
  if ( (!con) || (!msg) )
    return -EINVAL;

  switch(msg->command) {
  case IPMSG_NOOPERATION:
    break;
  case IPMSG_BR_ENTRY:
    dbg_out("Dispatch br_entry\n");
    ipmsg_proc_br_entry(con,msg);
    break;
  case IPMSG_BR_EXIT:
    dbg_out("Dispatch br_exit\n");
    ipmsg_proc_br_exit(con,msg);
    break;
  case IPMSG_ANSENTRY:
    dbg_out("Dispatch ans_entry\n");
    ipmsg_proc_ans_entry(con,msg);
    break;
  case IPMSG_BR_ABSENCE:
    dbg_out("Dispatch br_absense\n");
    ipmsg_proc_br_absence(con,msg);
    break;
  case IPMSG_BR_ISGETLIST:
    dbg_out("Dispatch isget_list2\n");
    if (hostinfo_refer_ipmsg_is_allow_hlist())
      ipmsg_proc_br_isgetlist2(con,msg);
    break;
  case IPMSG_OKGETLIST:
    dbg_out("Dispatch okget_list\n");
    if (hostinfo_refer_ipmsg_is_get_hlist())    
      ipmsg_proc_okgetlist(con,msg);
    break;
  case IPMSG_GETLIST:
    dbg_out("Dispatch get_list\n");
    if (hostinfo_refer_ipmsg_is_allow_hlist())
      ipmsg_proc_getlist(con,msg);
    else
      dbg_out("Discard getlist request\n");
    break;
  case IPMSG_ANSLIST:
    dbg_out("Dispatch ans_list\n");
    ipmsg_proc_anslist(con,msg);
    break;
  case IPMSG_BR_ISGETLIST2:
    dbg_out("Dispatch br_isget_list2\n");
    if (hostinfo_refer_ipmsg_is_allow_hlist())
      ipmsg_proc_br_isgetlist2(con,msg);
    break;
  case IPMSG_SENDMSG:
    dbg_out("Dispatch send_message\n");
    ipmsg_proc_send_msg(con,msg);
    break;
  case IPMSG_RECVMSG:
    dbg_out("Dispatch recv_message\n");
    ipmsg_proc_recv_msg(con,msg);
    break;
  case IPMSG_READMSG:
    dbg_out("Dispatch read_message\n");
    ipmsg_proc_read_msg(con,msg);
    break;
  case IPMSG_DELMSG:
    dbg_out("Dispatch delete_message\n");
    break;
  case IPMSG_ANSREADMSG:
    dbg_out("Dispatch ans_read_message\n");
    break;
  case IPMSG_GETINFO:
    dbg_out("Dispatch get_info\n");
    ipmsg_proc_send_version_info(con,msg);
    break;
  case IPMSG_SENDINFO:
    dbg_out("Dispatch send_info\n");
    ipmsg_proc_send_info(con,msg);
    break;
  case IPMSG_GETABSENCEINFO:
    dbg_out("Dispatch get_absence_info\n");
    ipmsg_proc_get_absence_info(con,msg);
    break;
  case IPMSG_SENDABSENCEINFO:
    dbg_out("Dispatch send_absence_info\n");
    ipmsg_proc_send_absence_info(con,msg);
    break;
  case IPMSG_RELEASEFILES:
    dbg_out("Dispatch release files\n");
    ipmsg_proc_release_files_msg(con,msg);
    break;
  case IPMSG_GETPUBKEY:
    dbg_out("Dispatch get public key\n");
    ipmsg_proc_get_public_key(con,msg);
    break;
  case IPMSG_ANSPUBKEY:
    dbg_out("Dispatch ans public key\n");
    ipmsg_proc_anspubkey(con,msg);
    break;
  default:
    dbg_out("Can not dispatch unknown:%x\n",msg->command);
    /* Unknown command */
    break;
  }
  return 0;
}
